-
-
Notifications
You must be signed in to change notification settings - Fork 344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement auto-switching to English when the option is enabled #3366
Conversation
This comment has been minimized.
This comment has been minimized.
🥷 Code experts: onesounds, Jack251970 onesounds, Jack251970 have most 👩💻 activity in the files. See details
Activity based on git-commit:
Knowledge based on git-blame: To learn more about /:\ gitStream - Visit our Docs |
Be a legend 🏆 by adding a before and after screenshot of the changes you made, especially if they are around UI/UX. |
📝 WalkthroughWalkthroughThe changes add automatic keyboard layout management based on the user’s "StartWithEnglishMode" setting. When the main window is shown, the launcher switches to an English keyboard layout if enabled, and it restores the previous layout when hidden. Updates were made in both UI logic and underlying native method handling to support this behavior. Changes
Assessment against linked issues
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (6)
Flow.Launcher/Helper/KeyboardLayoutHelper.cs (4)
1-66
: Good implementation of Windows APIs for keyboard layout managementThe implementation correctly uses P/Invoke to access the necessary Windows APIs for keyboard layout management. The
FindEnglishKeyboardLayout
method effectively retrieves and filters keyboard layouts to identify English ones.However, the current language detection method could be improved:
Consider using explicit English language IDs instead of string comparison for more robust language detection. The current approach using string contains "english" might not work reliably across all Windows versions and language configurations.
- private const string LAYOUT_ENGLISH_SEARCH = "english"; + // English language ID (0x0409 = US English, 0x0809 = UK English, etc.) + private static readonly uint[] ENGLISH_LANGUAGE_IDS = { 0x0409, 0x0809, 0x0c09, 0x1009, 0x1409, 0x1809, 0x1c09, 0x2009, 0x2409, 0x2809, 0x2c09, 0x3009, 0x3409 }; private static IntPtr FindEnglishKeyboardLayout() { // Get the number of keyboard layouts var count = GetKeyboardLayoutList(0, null); if (count <= 0) return IntPtr.Zero; // Get all keyboard layouts var keyboardLayouts = new IntPtr[count]; GetKeyboardLayoutList(count, keyboardLayouts); // Look for any English keyboard layout foreach (var layout in keyboardLayouts) { // The lower word contains the language identifier var langId = (uint)layout.ToInt32() & 0xFFFF; - - // Get language name for the layout - var sb = new StringBuilder(256); - GetLocaleInfoA(langId, LOCALE_SLANGUAGE, sb, sb.Capacity); - var langName = sb.ToString().ToLowerInvariant(); - - // Check if it's an English layout - if (langName.Contains(LAYOUT_ENGLISH_SEARCH)) + // Check if it's an English language ID + if (Array.IndexOf(ENGLISH_LANGUAGE_IDS, langId) >= 0) { return layout; } } return IntPtr.Zero; }
68-87
: Consider adding thread safety to the layout switching mechanismThe
_previousLayout
static field is shared and could cause race conditions if layout switching is triggered from multiple threads simultaneously.Add thread synchronization to prevent potential race conditions:
- private static IntPtr _previousLayout; + private static IntPtr _previousLayout; + private static readonly object _layoutLock = new object(); public static void SetEnglishKeyboardLayout() { - // Find an installed English layout - var englishLayout = FindEnglishKeyboardLayout(); - - // No installed English layout found - if (englishLayout == IntPtr.Zero) return; - - var hwnd = GetForegroundWindow(); - var threadId = GetWindowThreadProcessId(hwnd, IntPtr.Zero); - - // Store current keyboard layout - _previousLayout = GetKeyboardLayout(threadId) & 0xFFFF; - - // Switch to English layout - ActivateKeyboardLayout(englishLayout, 0); + lock (_layoutLock) + { + // Find an installed English layout + var englishLayout = FindEnglishKeyboardLayout(); + + // No installed English layout found + if (englishLayout == IntPtr.Zero) return; + + var hwnd = GetForegroundWindow(); + var threadId = GetWindowThreadProcessId(hwnd, IntPtr.Zero); + + // Store current keyboard layout + _previousLayout = GetKeyboardLayout(threadId); + + // Switch to English layout + ActivateKeyboardLayout(englishLayout, 0); + } }Also, the bitwise AND operation with 0xFFFF might be unnecessary when storing the layout handle. This operation extracts only the language ID portion but discards the keyboard type information.
89-94
: Apply thread safety to SetPreviousKeyboardLayout method as wellSimilar to the
SetEnglishKeyboardLayout
method, this method should also be thread-safe.public static void SetPreviousKeyboardLayout() { - if (_previousLayout == IntPtr.Zero) return; - ActivateKeyboardLayout(_previousLayout, 0); - _previousLayout = IntPtr.Zero; + lock (_layoutLock) + { + if (_previousLayout == IntPtr.Zero) return; + ActivateKeyboardLayout(_previousLayout, 0); + _previousLayout = IntPtr.Zero; + } }
7-95
: Add error handling and logging for diagnosticsSince this is an experimental feature and involves Windows API calls that might fail, consider adding error handling and logging.
Add try-catch blocks and logging to help with debugging:
public static void SetEnglishKeyboardLayout() { + try + { // Find an installed English layout var englishLayout = FindEnglishKeyboardLayout(); // No installed English layout found if (englishLayout == IntPtr.Zero) return; var hwnd = GetForegroundWindow(); var threadId = GetWindowThreadProcessId(hwnd, IntPtr.Zero); // Store current keyboard layout _previousLayout = GetKeyboardLayout(threadId) & 0xFFFF; // Switch to English layout ActivateKeyboardLayout(englishLayout, 0); + } + catch (Exception ex) + { + // Log the exception but don't crash the application + Log.Error("KeyboardLayoutHelper", $"Error switching to English layout: {ex.Message}", ex); + } } public static void SetPreviousKeyboardLayout() { + try + { if (_previousLayout == IntPtr.Zero) return; ActivateKeyboardLayout(_previousLayout, 0); _previousLayout = IntPtr.Zero; + } + catch (Exception ex) + { + // Log the exception but don't crash the application + Log.Error("KeyboardLayoutHelper", $"Error restoring previous layout: {ex.Message}", ex); + } }Also consider adding a simple way to detect if an English layout is already active to avoid unnecessary switching:
public static bool IsEnglishLayoutActive() { try { var hwnd = GetForegroundWindow(); var threadId = GetWindowThreadProcessId(hwnd, IntPtr.Zero); var currentLayout = GetKeyboardLayout(threadId); var langId = (uint)currentLayout.ToInt32() & 0xFFFF; // Check if it's already an English language ID return Array.IndexOf(ENGLISH_LANGUAGE_IDS, langId) >= 0; } catch (Exception ex) { Log.Error("KeyboardLayoutHelper", $"Error checking if English layout is active: {ex.Message}", ex); return false; } }Flow.Launcher/ViewModel/MainViewModel.cs (2)
1377-1382
: Good integration of keyboard layout switching on window showThe implementation correctly calls
KeyboardLayoutHelper.SetEnglishKeyboardLayout()
whenStartWithEnglishMode
is true, which fulfills the PR objective.Consider adding a check to avoid unnecessary layout switching if English is already the active layout:
if (StartWithEnglishMode) { + // Only switch if not already using English layout + if (!KeyboardLayoutHelper.IsEnglishLayoutActive()) KeyboardLayoutHelper.SetEnglishKeyboardLayout(); }This would require adding the
IsEnglishLayoutActive()
method toKeyboardLayoutHelper
as suggested in the previous comment.
1377-1455
: Consider adding exception handling for keyboard layout operationsSince keyboard layout operations involve native API calls that could potentially fail, adding try-catch blocks would make the code more robust.
Add try-catch blocks around keyboard layout operations to prevent exceptions from affecting the main application flow:
if (StartWithEnglishMode) { + try + { KeyboardLayoutHelper.SetEnglishKeyboardLayout(); + } + catch (Exception ex) + { + Log.Error("MainViewModel", $"Error setting English keyboard layout: {ex.Message}", ex); + // Continue execution despite the error + } }And similarly for the hide method:
if (StartWithEnglishMode) { + try + { KeyboardLayoutHelper.SetPreviousKeyboardLayout(); + } + catch (Exception ex) + { + Log.Error("MainViewModel", $"Error restoring previous keyboard layout: {ex.Message}", ex); + // Continue execution despite the error + } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
Flow.Launcher/Helper/KeyboardLayoutHelper.cs
(1 hunks)Flow.Launcher/ViewModel/MainViewModel.cs
(3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (7)
- GitHub Check: gitStream workflow automation
- GitHub Check: gitStream workflow automation
- GitHub Check: gitStream.cm
- GitHub Check: gitStream.cm
- GitHub Check: gitStream.cm
- GitHub Check: gitStream.cm
- GitHub Check: gitStream.cm
🔇 Additional comments (2)
Flow.Launcher/ViewModel/MainViewModel.cs (2)
1451-1455
: Good implementation of layout restoration on window hideThe code appropriately restores the previous keyboard layout when hiding the window, fulfilling the PR objective.
17-17
: Appropriate namespace inclusionThe using directive for
Flow.Launcher.Helper
is correctly added to access the newKeyboardLayoutHelper
class.
… of relying on layout name as a string
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I have no idea how to test it. So could you please test it to check if PInvoke introduction has broken sth? |
Yes, with this version the keyboard layout switches to English, but seems to never switch back. |
To test it, change your keyboard layout to a non-English one, then open Flow. It should switch to English. Close Flow. It should switch to the orignial layout. |
Get it |
This comment has been minimized.
This comment has been minimized.
Fixed. Now it works well. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (5)
Flow.Launcher.Infrastructure/Win32Helper.cs (5)
326-331
: Document the English language IDsThe array contains language IDs for different English variants, but it would be helpful to add a comment explaining what some of these values represent (e.g., US English, UK English, etc.) for better maintainability.
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/70feba9f-294e-491e-b6eb-56532684c37f private static readonly uint[] EnglishLanguageIds = { - 0x0009, 0x0409, 0x0809, 0x0C09, 0x1009, 0x1409, 0x1809, 0x1C09, 0x2009, 0x2409, 0x2809, 0x2C09, 0x3009, - 0x3409, 0x3C09, 0x4009, 0x4409, 0x4809, 0x4C09, + 0x0009, // English (Neutral) + 0x0409, // English (United States) + 0x0809, // English (United Kingdom) + 0x0C09, // English (Australia) + 0x1009, // English (Canada) + 0x1409, // English (New Zealand) + 0x1809, // English (Ireland) + 0x1C09, // English (South Africa) + 0x2009, // English (Jamaica) + 0x2409, // English (Caribbean) + 0x2809, // English (Belize) + 0x2C09, // English (Trinidad and Tobago) + 0x3009, // English (Zimbabwe) + 0x3409, // English (Philippines) + 0x3C09, // English (Hong Kong SAR) + 0x4009, // English (India) + 0x4409, // English (Malaysia) + 0x4809, // English (Singapore) + 0x4C09, // English (Nigeria) };
333-336
: Document the IME language IDsSimilar to the English language IDs, adding comments to describe what languages these IME IDs represent would improve code maintainability.
private static readonly uint[] ImeLanguageIds = { - 0x0004, 0x7804, 0x0804, 0x1004, 0x7C04, 0x0C04, 0x1404, 0x0404, 0x0011, 0x0411, 0x0012, 0x0412, + 0x0004, // Chinese (Neutral) + 0x7804, // Chinese (Simplified, China) + 0x0804, // Chinese (Simplified) + 0x1004, // Chinese (Singapore) + 0x7C04, // Chinese (Traditional, Hong Kong SAR) + 0x0C04, // Chinese (Traditional, Hong Kong) + 0x1404, // Chinese (Traditional, Macau SAR) + 0x0404, // Chinese (Traditional, Taiwan) + 0x0011, // Japanese (Neutral) + 0x0411, // Japanese + 0x0012, // Korean (Neutral) + 0x0412, // Korean };
343-370
: Add error handling for GetKeyboardLayoutListThe method correctly handles the case when no keyboard layouts are found, but it doesn't check if the second call to
GetKeyboardLayoutList
fails. Consider adding error handling for this call as well.private static unsafe HKL FindEnglishKeyboardLayout() { // Get the number of keyboard layouts int count = PInvoke.GetKeyboardLayoutList(0, null); if (count <= 0) return HKL.Null; // Get all keyboard layouts var handles = new HKL[count]; fixed (HKL* h = handles) { - _ = PInvoke.GetKeyboardLayoutList(count, h); + int result = PInvoke.GetKeyboardLayoutList(count, h); + if (result <= 0 || result != count) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } } // Look for any English keyboard layout foreach (var hkl in handles) { // The lower word contains the language identifier var langId = (uint)hkl.Value & KeyboardLayoutLoWord; // Check if it's an English layout if (EnglishLanguageIds.Contains(langId)) { return hkl; } } return HKL.Null; }
372-402
: Add XML documentation for public methodSince
SwitchToEnglishKeyboardLayout
is a public method, it should have XML documentation to describe its purpose, parameters, and any exceptions it may throw.+/// <summary> +/// Switches the keyboard layout to English if available. +/// </summary> +/// <param name="backupPrevious">If true, the current keyboard layout will be stored for later restoration.</param> +/// <exception cref="Win32Exception">Thrown when there's an error getting the window thread process ID.</exception> public static unsafe void SwitchToEnglishKeyboardLayout(bool backupPrevious) { // Function implementation... }Also, the comment for IME handling is informative, but could be made more visible as part of the method documentation.
404-416
: Add XML documentation and NULL check for GetForegroundWindowSimilar to the previous method,
RestorePreviousKeyboardLayout
should have XML documentation. Additionally, add a check to ensureGetForegroundWindow()
doesn't return null before sending a message to it.+/// <summary> +/// Restores the previously backed-up keyboard layout. +/// </summary> public static void RestorePreviousKeyboardLayout() { if (_previousLayout == HKL.Null) return; var hwnd = PInvoke.GetForegroundWindow(); + if (hwnd == HWND.Null) return; PInvoke.PostMessage( hwnd, PInvoke.WM_INPUTLANGCHANGEREQUEST, PInvoke.INPUTLANGCHANGE_FORWARD, _previousLayout.Value ); _previousLayout = HKL.Null; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
Flow.Launcher.Infrastructure/NativeMethods.txt
(1 hunks)Flow.Launcher.Infrastructure/Win32Helper.cs
(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- Flow.Launcher.Infrastructure/NativeMethods.txt
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: gitStream.cm
🔇 Additional comments (3)
Flow.Launcher.Infrastructure/Win32Helper.cs (3)
3-3
: LGTM: Added System.Linq for the Contains methodThe addition of the System.Linq namespace is appropriate for the new code that uses the Contains method on arrays.
389-392
: LGTM: Good handling of IME languagesThe code correctly identifies IME languages and handles them specially, which aligns with the PR objective of properly supporting both IME and non-IME languages.
324-418
: Overall implementation looks goodThe implementation successfully addresses the PR objective of switching to English keyboard layout when Flow Launcher is opened and restoring the previous layout when closed. The code is well-structured, handles edge cases appropriately, and includes special handling for IME languages.
Some minor improvements could be made to documentation and error handling, but the core functionality appears solid.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
Flow.Launcher.Infrastructure/Win32Helper.cs (2)
376-401
: Clarify the IME handling rationale with additional commentsThe code intentionally returns early when an IME layout is detected instead of switching layouts. Although there is a brief comment about this behavior, it would be helpful to provide more context about why this special handling is necessary.
Consider adding a more detailed comment like:
// If the current layout has an IME mode, disable it without switching to another layout. // This is needed because for languages with IME mode, Flow Launcher just temporarily disables // the IME mode instead of switching to another layout. +// IME languages require specialized input method handling and fully switching them could disrupt +// the user experience, so we maintain the existing behavior for backward compatibility.
402-411
: Consider adding error handling for ActivateKeyboardLayoutThe call to PInvoke.ActivateKeyboardLayout might fail, but there's no error checking or handling.
Consider adding error handling:
// Switch to English layout -PInvoke.ActivateKeyboardLayout(enHKL, 0); +var result = PInvoke.ActivateKeyboardLayout(enHKL, 0); +if (result == HKL.Null) +{ + // Log the error or handle the failure + var errorCode = Marshal.GetLastWin32Error(); + System.Diagnostics.Debug.WriteLine($"Failed to activate keyboard layout: {errorCode}"); +}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Flow.Launcher.Infrastructure/Win32Helper.cs
(3 hunks)
🔇 Additional comments (3)
Flow.Launcher.Infrastructure/Win32Helper.cs (3)
3-3
: LGTM - Using System.Linq for array operationsThe addition of System.Linq is necessary for the Contains() method used with the language ID arrays.
326-336
: Well-documented language ID referencesGood job providing the Microsoft documentation link for the language identifiers and clearly separating English from IME language IDs.
417-431
: Good use of window-specific messaging for layout restorationThe implementation correctly posts the input language change message to the specific foreground window rather than broadcasting it system-wide, which aligns with previous review feedback.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
Flow.Launcher.Infrastructure/Win32Helper.cs (3)
327-336
: Language ID arrays look good, consider adding a comment for selected IDs.The implementation of the static readonly arrays for language IDs is appropriate and includes a helpful link to documentation.
Consider adding a brief comment explaining which languages are covered by the
ImeLanguageIds
array (Chinese variants, Japanese, Korean) to improve code readability for future maintainers.
384-401
: Early return for IME layouts needs clearer documentation.The code correctly identifies and handles IME layouts differently, but the comment could be more explanatory.
The comment on lines 393-395 mentions that "Flow Launcher just temporarily disables the IME mode instead of switching to another layout," but it doesn't explain why this approach is taken. Consider expanding the comment to clarify the rationale, which would help future developers understand this design decision.
402-411
: Add error handling for ActivateKeyboardLayout.The keyboard layout activation lacks error handling.
The call to
ActivateKeyboardLayout
at line 410 should include error handling to catch potential failures. Consider adding a check for the return value and throwing an exception or logging a warning if it fails:-PInvoke.ActivateKeyboardLayout(enHKL, 0); +var result = PInvoke.ActivateKeyboardLayout(enHKL, 0); +if (result == HKL.Null) +{ + // Log warning or throw exception + throw new Win32Exception(Marshal.GetLastWin32Error()); +}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Flow.Launcher.Infrastructure/Win32Helper.cs
(3 hunks)
🔇 Additional comments (3)
Flow.Launcher.Infrastructure/Win32Helper.cs (3)
343-374
: FindEnglishKeyboardLayout implementation is correct and robust.The method correctly retrieves all keyboard layouts and finds the first English one based on the language ID.
The error handling at lines 354-357 has been correctly implemented to throw an exception when the API call fails (returns 0).
390-392
: Process ID capture still missing in GetWindowThreadProcessId call.The function doesn't capture the process ID returned by GetWindowThreadProcessId.
As noted in a previous review comment, GetWindowThreadProcessId is designed to return both the thread ID and the process ID via an out parameter. Consider capturing the process ID for enhanced robustness:
-var threadId = PInvoke.GetWindowThreadProcessId(PInvoke.GetForegroundWindow()); +uint processId; +var threadId = PInvoke.GetWindowThreadProcessId(PInvoke.GetForegroundWindow(), out processId);Even if you don't need the process ID now, capturing it follows the API's intended usage and improves future maintainability.
338-341
:✅ Verification successful
LGTM, but consider multi-window scenarios.
The constant and static field implementation is correct for storing and manipulating keyboard layouts.
Note that using a static field to store the previous layout might cause issues in multi-window scenarios. If multiple Flow Launcher windows can be opened simultaneously, they could interfere with each other's keyboard layout restoration. Can you verify if this is a valid concern for your application architecture?
🏁 Script executed:
#!/bin/bash # Search for multi-window usage in the codebase echo "Checking for multiple window instances or window management code:" rg -p "new\s+(?:MainWindow|Window)\(" --type cs -A 2 -B 2Length of output: 433
Action: Confirming static field usage is safe under the current single-window design.
After verifying with the code search, it appears that Flow Launcher currently creates only a single instance of MainWindow (see Flow.Launcher/App.xaml.cs, line 140). Therefore, while the use of a static field to store the previous keyboard layout could be problematic in a true multi-window scenario, the present architecture does not exhibit such behavior. If multi-window support is added in the future, this design decision should be revisited.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
Flow.Launcher.Infrastructure/Win32Helper.cs (1)
417-435
: 🛠️ Refactor suggestionUse SendMessage instead of PostMessage for reliable layout restoration.
Using PostMessage for keyboard layout restoration can lead to race conditions since it's asynchronous. If another keyboard layout change occurs before the message is processed, the restoration might not work as expected.
- PInvoke.PostMessage( - hwnd, - PInvoke.WM_INPUTLANGCHANGEREQUEST, - PInvoke.INPUTLANGCHANGE_FORWARD, - _previousLayout.Value - ); + PInvoke.SendMessage( + hwnd, + PInvoke.WM_INPUTLANGCHANGEREQUEST, + PInvoke.INPUTLANGCHANGE_FORWARD, + _previousLayout.Value + );
🧹 Nitpick comments (2)
Flow.Launcher.Infrastructure/Win32Helper.cs (2)
394-395
: Consider capturing process ID for completeness.The GetWindowThreadProcessId function returns both the thread ID and the process ID via an out parameter. Even if you don't need the process ID right now, capturing it would make the code more complete and maintainable.
- var threadId = PInvoke.GetWindowThreadProcessId(hwnd); - if (threadId == 0) throw new Win32Exception(Marshal.GetLastWin32Error()); + uint processId; + var threadId = PInvoke.GetWindowThreadProcessId(hwnd, out processId); + if (threadId == 0) throw new Win32Exception(Marshal.GetLastWin32Error());
428-433
: Add error handling for message sending.The code doesn't check if the message was successfully sent or handle potential errors. Consider adding error handling for the message sending operation.
- PInvoke.PostMessage( - hwnd, - PInvoke.WM_INPUTLANGCHANGEREQUEST, - PInvoke.INPUTLANGCHANGE_FORWARD, - _previousLayout.Value - ); + bool success = PInvoke.PostMessage( + hwnd, + PInvoke.WM_INPUTLANGCHANGEREQUEST, + PInvoke.INPUTLANGCHANGE_FORWARD, + _previousLayout.Value + ); + + if (!success) + { + // Log error or handle the failure + var errorCode = Marshal.GetLastWin32Error(); + // Could throw or log: new Win32Exception(errorCode) + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
Flow.Launcher.Infrastructure/NativeMethods.txt
(1 hunks)Flow.Launcher.Infrastructure/Win32Helper.cs
(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- Flow.Launcher.Infrastructure/NativeMethods.txt
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: gitStream.cm
🔇 Additional comments (5)
Flow.Launcher.Infrastructure/Win32Helper.cs (5)
3-3
: Added System.Linq import for array operations.The addition of the System.Linq namespace is appropriate for using the Contains method on arrays in the keyboard layout code.
11-11
: Added KeyboardAndMouse namespace for keyboard API access.Good addition of the Windows.Win32.UI.Input.KeyboardAndMouse namespace to access the keyboard-related Win32 APIs used in the new keyboard layout functionality.
324-337
: Language ID constants are well-organized and documented.The language ID constants for both English and IME languages are well-organized and properly documented with a link to official Microsoft documentation, which will help with future maintenance.
343-374
: FindEnglishKeyboardLayout implementation looks correct.The method properly retrieves all installed keyboard layouts and searches for an English one. The error handling has been fixed from previous reviews.
376-415
: Good implementation of language switching with IME handling.The implementation correctly finds an English layout, handles IME languages appropriately, and activates the English layout when needed. The comments clearly explain the special handling for IME languages.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
Flow.Launcher.Infrastructure/Win32Helper.cs (1)
389-404
: 🛠️ Refactor suggestionUse SendMessage instead of PostMessage for more reliable layout switching
Using
PostMessage
for keyboard layout restoration creates potential race conditions since it's asynchronous and doesn't wait for the message to be processed. There's also no error handling for the message sending.Replace with
SendMessage
to ensure synchronous execution and add error handling:- PInvoke.PostMessage( - hwnd, - PInvoke.WM_INPUTLANGCHANGEREQUEST, - PInvoke.INPUTLANGCHANGE_FORWARD, - _previousLayout.Value - ); + var result = PInvoke.SendMessage( + hwnd, + PInvoke.WM_INPUTLANGCHANGEREQUEST, + PInvoke.INPUTLANGCHANGE_FORWARD, + _previousLayout.Value + ); + + if (result.Value == IntPtr.Zero) + { + var error = Marshal.GetLastWin32Error(); + if (error != 0) + { + System.Diagnostics.Debug.WriteLine($"Failed to restore keyboard layout: {new Win32Exception(error).Message}"); + } + }
🧹 Nitpick comments (1)
Flow.Launcher.Infrastructure/Win32Helper.cs (1)
347-383
: Method logic looks good, but add a configuration optionThe implementation of
SwitchToEnglishKeyboardLayout
correctly finds and activates the English keyboard layout. The early exit for IME layouts is also appropriate as it maintains existing behavior.Consider pulling the IME language tags into a configuration option that can be extended if needed, rather than hardcoding them:
- private static readonly string[] ImeLanguageTags = - { - "zh", // Chinese - "ja", // Japanese - "ko", // Korean - }; + private static readonly string[] ImeLanguageTags = Settings.Instance.ImeLanguageTags ?? new[] + { + "zh", // Chinese + "ja", // Japanese + "ko", // Korean + };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
Flow.Launcher.Infrastructure/NativeMethods.txt
(1 hunks)Flow.Launcher.Infrastructure/Win32Helper.cs
(3 hunks)
🔇 Additional comments (3)
Flow.Launcher.Infrastructure/Win32Helper.cs (2)
417-423
: Error handling for GetKeyboardLayoutList is correctThe correction to check for
result == 0
to detect an error is properly implemented.
453-489
:❓ Verification inconclusive
The registry-based approach for transient language identifiers is well-implemented
The method uses a thoughtful approach to handle transient language identifiers by checking the registry. The fallback to
CultureInfo.GetCultureInfo
is also appropriate.Consider verifying that this code runs with proper permissions by testing on systems with restricted registry access:
🏁 Script executed:
#!/bin/bash # Check if the registry key is accessible by running a similar access pattern # This is to verify the code will work on systems with restricted registry access echo "Checking registry access for keyboard layouts:" rg "OpenSubKey.*UserProfile" --type cs -A 5 -B 5 echo "" echo "Looking for other registry access patterns that might provide insights:" rg "Registry\.CurrentUser\.OpenSubKey" --type cs -A 2 -B 2Length of output: 5672
Registry Access Verification: Confirm Behavior Under Restricted Permissions
The registry-based approach for managing transient language identifiers in
Flow.Launcher.Infrastructure/Win32Helper.cs
(lines 453–489) remains well-implemented. The method appropriately checks the registry for transient identifiers and correctly falls back toCultureInfo.GetCultureInfo
when needed. Notably, our repository-wide search confirms that similar registry access patterns exist (e.g., inFlow.Launcher/Resources/Pages/WelcomePage5.xaml.cs
andFlow.Launcher/Helper/AutoStartup.cs
), reinforcing consistency across the codebase.However, please verify that systems with restricted registry access still grant the necessary permissions for these operations. Running targeted tests on such environments will provide additional confidence in the robustness of this implementation.
Flow.Launcher.Infrastructure/NativeMethods.txt (1)
51-61
: Native method additions are complete and well-organizedThe additions to NativeMethods.txt properly document all the Windows API functions and constants used in the new keyboard layout management functionality.
@check-spelling-bot Report🔴 Please reviewSee the 📂 files view, the 📜action log, or 📝 job summary for details.
See ❌ Event descriptions for more information. If the flagged items are 🤯 false positivesIf items relate to a ...
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
Flow.Launcher.Infrastructure/Win32Helper.cs (2)
389-404
: Consider using SendMessage instead of PostMessage for layout restorationThe implementation uses PostMessage which is asynchronous and may introduce race conditions if other keyboard layout changes occur before the message is processed.
- PInvoke.PostMessage( - hwnd, - PInvoke.WM_INPUTLANGCHANGEREQUEST, - PInvoke.INPUTLANGCHANGE_FORWARD, - _previousLayout.Value - ); + PInvoke.SendMessage( + hwnd, + PInvoke.WM_INPUTLANGCHANGEREQUEST, + PInvoke.INPUTLANGCHANGE_FORWARD, + _previousLayout.Value + );
361-363
: Capture Process ID for enhanced error handlingThe current code only captures and validates the thread ID. For improved error handling and debugging, consider capturing the process ID as well.
- var threadId = PInvoke.GetWindowThreadProcessId(hwnd); - if (threadId == 0) throw new Win32Exception(Marshal.GetLastWin32Error()); + uint processId; + var threadId = PInvoke.GetWindowThreadProcessId(hwnd, out processId); + if (threadId == 0) throw new Win32Exception(Marshal.GetLastWin32Error());
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Flow.Launcher.Infrastructure/Win32Helper.cs
(3 hunks)
🔇 Additional comments (3)
Flow.Launcher.Infrastructure/Win32Helper.cs (3)
325-342
: Good keyboard layout management setup with clear organizationThe new constants and static variables for keyboard layout management are well-organized and properly documented. Good use of language tags instead of hardcoded values and clear separation with a dedicated region.
344-383
: Comprehensive keyboard layout switching implementationThe implementation properly handles the core functionality of switching to an English keyboard layout while preserving the previous layout. The early return for IME layouts (lines 370-376) aligns with the PR goal of ensuring backward compatibility for IME languages.
442-488
: Well-documented approach to handling language tagsThe GetLanguageTag method includes thorough documentation about potential issues with transient language identifiers and the workaround implemented. The warning about this approach potentially breaking in future Windows versions is appropriate.
@Yusyuriv I assume it's confirmed no memory leaks with the the Win API usage? |
@jjw24 As far as I can tell, there are no memory leaks, everything's properly disposed of. @Jack251970 Just in case, can you confirm this? |
Warning
DO NOT MERGE YET. Highly experimental, needs thorough testing with both IME and non-IME languages; also needs someone to confirm there are no memory leaks with using the native Windows APIs.
Fixes #2108, fixes #2569, fixes #3126.
What it does
When the
Always Start Typing in English Mode
option is enabled, when the Flow Launcher window opens, it remembers the current keyboard layout and then switches to English. When the window hides, it restores the remembered keyboard layout.Testing I've done
Known issues
When it gets closed via Esc, Alt+Space, or Enter, it restores the keyboard layout correctly, but when the window loses focus, the layout does not get restored.
I haven't found a way to fix this, but I think it's still better than not having this functionality at all.Fixed.
Things to consider